﻿using System.Linq.Expressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Hvam.SharePoint.Search.Tests
{
    public static class AssertExpression
    {
        public static void AreEqual(Expression expected, Expression actual)
        {
            if (expected == null && actual == null)
            {
                return;
            }

            if ((expected == null && actual != null) ||
                (expected != null && actual == null))
            {
                Assert.Fail();
            }

            Assert.AreEqual(expected.NodeType, actual.NodeType);

            Assert.AreEqual(expected.GetType(), actual.GetType());

            var type = actual.GetType();

            if (type == typeof(BinaryExpression) || type.IsSubclassOf(typeof(BinaryExpression)))
            {
                var expectedBinaryExpression = (BinaryExpression)expected;
                var actualBinaryExpression = (BinaryExpression)actual;

                AreEqual(expectedBinaryExpression.Left, actualBinaryExpression.Left);
                AreEqual(expectedBinaryExpression.Right, actualBinaryExpression.Right);
            }

            if (type == typeof(ConstantExpression))
            {
                var expectedConstantExpression = (ConstantExpression) expected;
                var actualConstantExpression = (ConstantExpression) actual;

                Assert.AreEqual(expectedConstantExpression.Value, actualConstantExpression.Value);
            }

            if (type == typeof(MethodCallExpression))
            {
                var expectedMethodCallExpression = (MethodCallExpression) expected;
                var actualMethodCallExpression = (MethodCallExpression) actual;

                Assert.AreEqual(expectedMethodCallExpression.Arguments.Count, actualMethodCallExpression.Arguments.Count);
                var arguments = actualMethodCallExpression.Arguments.Count;
                for (var i = 0; i < arguments; i++)
                {
                    var expectedArgument = expectedMethodCallExpression.Arguments[i];
                    var actualArgument = actualMethodCallExpression.Arguments[i];

                    AreEqual(expectedArgument, actualArgument);
                }

                Assert.AreEqual(expectedMethodCallExpression.Method.Name, actualMethodCallExpression.Method.Name);

                AreEqual(expectedMethodCallExpression.Object, actualMethodCallExpression.Object);
            }
        }
    }
}
